/*
 * CCVisu is a tool for visual graph clustering
 * and general force-directed graph layout.
 * This file is part of CCVisu.
 *
 * Copyright (C) 2005-2012  Dirk Beyer
 *
 * CCVisu is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * CCVisu is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with CCVisu; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Please find the GNU Lesser General Public License in file
 * license_lgpl.txt or http://www.gnu.org/licenses/lgpl.txt
 *
 * Dirk Beyer    (firstname.lastname@uni-passau.de)
 * University of Passau, Bavaria, Germany
 */
package org.sosy_lab.ccvisu.ui.controlpanel;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

import java.awt.BorderLayout;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EventObject;
import java.util.List;
import java.util.Set;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListSelectionModel;

import org.sosy_lab.ccvisu.Options;
import org.sosy_lab.ccvisu.Options.OptionsEnum;
import org.sosy_lab.ccvisu.graph.GraphData;
import org.sosy_lab.ccvisu.graph.Relation;
import org.sosy_lab.ccvisu.graph.Tuple;
import org.sosy_lab.ccvisu.readers.filter.Filter;
import org.sosy_lab.ccvisu.readers.filter.IdentityFilter;
import org.sosy_lab.ccvisu.readers.filter.InternalRelations;
import org.sosy_lab.ccvisu.readers.filter.Uniq;
import org.sosy_lab.ccvisu.readers.filter.WhitelistRelationFilter;
import org.sosy_lab.ccvisu.ui.interfaces.GraphLoadedListener;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

@SuppressWarnings("serial")
public class ToolPanelFilters extends ControlPanel implements GraphLoadedListener {

  private final Options options;

  /** The relation prior to possible appliances of filters. */
  private Relation originalRelation;

  private final JCheckBox uniqueFilterCheckbox = new JCheckBox();
  private final JCheckBox internalRelsCheckbox = new JCheckBox();
  private final JButton   applyFilterButton    = new JButton("Apply filters");
  private final JButton   resetFilterButton    = new JButton("Reset filters");

  private final WhitelistRelationFilterChooser relationsFilterChooser = new WhitelistRelationFilterChooser();

  public ToolPanelFilters(Options options) {
    this.options = checkNotNull(options);
    this.originalRelation = checkNotNull(options.graph.getRelation());

    initComponents();

    options.graphLoadedListeners.add(this);
  }

  private void initComponents() {
    setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

    JPanel choicePanel = new JPanel(new GridBagLayout());
    add(choicePanel, BorderLayout.NORTH);

    uniqueFilterCheckbox.setText("Yes");
    addOptionControls(choicePanel, "Remove duplicated tuples?", uniqueFilterCheckbox);

    internalRelsCheckbox.setText("Yes");
    addOptionControls(choicePanel, "Internal relations only?", internalRelsCheckbox);

    addOptionControls(choicePanel, "Select relations:", relationsFilterChooser);

    add(Box.createVerticalStrut(11));
    add(applyFilterButton);

    add(Box.createVerticalStrut(7));
    add(resetFilterButton);

    resetFilters();
    assignListeners();

    applyFilterButton.setMnemonic('A');
  }

  private void assignListeners() {
    applyFilterButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        applyFiltering();
      }
    });

    resetFilterButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        resetFilters();
        applyFiltering();
      }
    });
  }

  private void applyFiltering() {
    List<Filter> filterQueue = Lists.newLinkedList();

    if (uniqueFilterCheckbox.isSelected()) {
      filterQueue.add(new Uniq());
    }

    if (internalRelsCheckbox.isSelected()) {
      filterQueue.add(new InternalRelations(options.verbosity));
    }

    filterQueue.add(relationsFilterChooser.createFilter());

    Relation filteredRelation = originalRelation;
    for (Filter filter : filterQueue) {
      // TODO: add some progress information
      filteredRelation = filter.apply(filteredRelation);
    }

    GraphData graph = options.graph;
    graph.applyRelation(filteredRelation, options.verbosity);

    // new nodes have been set and their positions are at (0, 0, 0).
    // To get a valid layout, randomize the nodes.
    graph.setNodesToRandomPositions(options.graph.getVertices(), options.getOption(OptionsEnum.dim).getInt());
    graph.notifyAboutLayoutChange(new EventObject(this));

    options.infoCollector.addMessage("After filtering, there are "
        + graph.getVertices().size() + " vertices and "
        + graph.getEdges().size() + " edges left.");
    options.infoCollector.addMessage("");
  }

  @Override
  public void onGraphLoaded(GraphData graph) {
    originalRelation = graph.getRelation();
    checkState(originalRelation != null);

    resetFilters();
  }

  private void resetFilters() {
    internalRelsCheckbox.setSelected(false);
    uniqueFilterCheckbox.setSelected(false);
    relationsFilterChooser.updateRelations();
  }

  private class WhitelistRelationFilterChooser extends JList<String> {

    public WhitelistRelationFilterChooser() {
      setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
      setVisibleRowCount(3);

      updateRelations();
    }

    public void updateRelations() {
      if (originalRelation == null || originalRelation.isEmpty()) {
        setEnabled(false);
        return;

      } else {
        setEnabled(true);
      }

      Set<String> uniqRelationsNames = Sets.newHashSet();

      for (Tuple tuple : originalRelation) {
        uniqRelationsNames.add(tuple.getRelationName());
      }

      setListData(uniqRelationsNames.toArray(new String[0]));

      // select all
      setSelectionInterval(0, uniqRelationsNames.size() - 1);
    }

    public Filter createFilter() {
      if (getSelectedIndices().length == 0) {
        return new IdentityFilter();
      }

      List<String> selectedValues = Lists.newLinkedList();
      for (String relName : getSelectedValuesList()) {
        selectedValues.add(relName);
      }

      return new WhitelistRelationFilter(selectedValues);
    }
  }
}
